#define _GNU_SOURCE
#include <unistd.h>
#include <fcntl.h>
#include <sys/wait.h>
#include <semaphore.h>
#include <sys/time.h>
#include <pthread.h>
#include <libavformat/avformat.h>
#include <libavutil/imgutils.h>
#include <libswscale/swscale.h>
#include <libavcodec/avcodec.h>
#include <x264.h>

#include "list.h"
#include "utils.h"

#define MAX_OUTPUT_NUM 10
#define X264_CTRL_SIZE 100

typedef struct x264_t x264_t;

//typedef struct x264_control
//{
//    int id;
//    int multi_version;
//    int used;
//    x264_t *h[MAX_OUTPUT_NUM];
//    int call_cnt;
//    int x_num;
//    int skip_call;
//    pthread_t tid;
//} x264_control_t;

typedef struct EncodeStruct
{
	int full_gop_buffer_id;
	int dst_width;
	int dst_height;
	int bitrate;
	enum AVPixelFormat dst_pix_fmt;
	char *output_filename;
	int thread_num;
	char output_dir[256];
	char output_format[16];
    
    // How many threads have been launched?
	int running_thread_cnt;
    sem_t thread_cnt_mutex;
    
    // How many frames have been encoded?
	int encoded_frame_cnt;
    sem_t encoded_frame_mutex;
    
    //whether this encode struct has finished?
    int finished;
}EncodeStruct;

typedef struct EncodeStructList
{
    EncodeStruct *es[10];
    int es_num;
} EncodeStructList;

typedef struct codec_t
{
    AVFormatContext *fmt_ctx;
    AVOutputFormat *out_fmt;
    AVStream *video_st;
    AVCodecContext *codec_ctx;
    AVCodec *codec;
    struct SwsContext *sws_ctx;
    AVFrame *src_frame;
    AVFrame *dst_frame;
    AVPacket pkt;

    x264_control_t *x264_ctrl;
} codec_t;

typedef struct Period
{
	long start;
	long end;
} Period;


struct time_record {
	struct timeval start;
	struct timeval end;
	unsigned long time_cost;
    char* function_name;
};

typedef struct time_record * TimeRecord;

typedef struct Gop
{
	int gop_id;
	int frame_cnt;   // the number of frames that actually exists
	int capacity;
	uint8_t *buf;
	uint8_t *valid;
	sem_t gop_mutex;
	int encoded_cnt; // how many times frames in this gop have been encoded
} Gop;

void initialize();

//encoding function declaration
inline int init_codec_ctx (codec_t *c, EncodeStruct *es);
inline int close_codec_ctx (codec_t *c);
int scale_and_encode_gop(Gop *g, EncodeStruct *es, codec_t *c);
int encode_and_scale_thread(void *arg);
int flush_multi_encoder (codec_t *c_list, unsigned int stream_idx, int num);
int flush_encoder (codec_t *c, EncodeStruct *es, unsigned int stream_idx);
int multi_encode_thread(void *arg);
int gop_multi_scale_enc(Gop *g, EncodeStructList *es_list, codec_t *c_list);
int initialize_encode_data();
EncodeStruct *slowest_es();
int all_finished();

void decode_flush(AVPacket* p_pkt, int video_stream_idx, AVCodecContext* video_dec_ctx, AVFrame* frame, uint8_t** video_dst_data, int* video_dst_linesize, Period* p);

//decoding function declaration
int initialize_decode_data();
int decode_period();
void decode_flush(AVPacket* p_pkt, int video_stream_idx, AVCodecContext* video_dec_ctx, AVFrame* frame, uint8_t** video_dst_data, int* video_dst_linesize, Period* p);
int decode();
int has_period();
Period* get_period();

//gop related function declartion
bool is_empty_Gop(Gop *g);
void free_Gop(Gop *g);
Gop *new_Gop(int gop_id, int gop_size);
Gop *find_gop_in_gop_buffer(int gop_id);
void add_gop_to_gop_buffer(Gop *g);
void remove_gop_from_gop_buffer(Gop *g);
int count_gop_in_full_gop_buffer();
void print_gop_id_of_full_gop_buffer();
void add_gop_to_full_gop_buffers(Gop *g);
void add_gop_to_full_gop_buffer(Gop *g, int full_gop_buffer_id);
Gop *remove_gop_from_full_gop_buffer(int full_gop_buffer_id);
void add_scaled_frame_to_gop_buffer(int pict_num, uint8_t **buf);
int add_remaining_frame_to_full_gop_buffer();

//core binding function declaration
void binding_core();
int get_core_id();
void core_binding_initialize();

//output function declaration
void print_average_fps();
void output_video(EncodeStruct *es);

void atomic_inc(int *var, int m, sem_t *lock);
void atomic_dec(int *var, int m, sem_t *lock);

//x264 function declaration
// x264_control_t *get_x264_ctrl(int x_num);
#define SUCCESS_CALL(msg, func, ...)    \
    do {                                \
        int ret = func;                 \
        if (ret < 0) {                   \
            printf(msg);                \
            return ret;                 \
        }                               \
    } while(0);
